package com.itmuch.providerdemo;

import com.itmuch.providerdemo.feign.BaiduFeignClient;
import com.itmuch.providerdemo.feign.ProviderDemoFeignClient;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.eureka.EurekaServiceInstance;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

// 船舱1
// thread-pool-1 corePoolSize=5, workQueue=5, maximumPoolSize=10
@Slf4j
@RestController
public class ConsumerTestController {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private DiscoveryClient discoveryClient;

    /**
     * 监控API的调用错误次数/错误率
     * 如果错误次数/错误率达到阈值，就跳闸
     * hystrix 5s/20次或50%
     * ===
     * 如何恢复？断路器三态转换
     *
     * @return
     */
    @GetMapping("consumer-hello")
    public String consumerHello() {
        // select * from registry where app = 'PROVIDER-DEMO'
//        List<String> services = this.discoveryClient.getServices();

        List<ServiceInstance> instances = this.discoveryClient.getInstances("PROVIDER-DEMO");

        int i = ThreadLocalRandom.current().nextInt(instances.size());

        ServiceInstance serviceInstance = instances.get(i);
        EurekaServiceInstance serviceInstance1 = (EurekaServiceInstance) serviceInstance;
        URI uri = serviceInstance1.getUri();
        String url = uri + "/hello";

        log.info("请求的url = {}", url);

        // 调用provider
        return "consumer:" + restTemplate.getForObject(
                url,
                String.class
        );
    }

    @Autowired
    private RestTemplate ribbonTemplate;

    /**
     * 1. 超时，不管请求是否能响应，1s就会释放线程
     * 2. 仓壁模式，会把这个方法放到独立的线程池，线程池的名称是类名；
     * 线程池的大小: corePoolSize=5, workQueue=5, maximumPoolSize=10
     * 3. 断路器，5s/20次或50%，就跳闸
     * 4. 限流？？？hystrix没有直接实现，而是用仓壁模式实现的
     * ====
     * resilience4j 超时、限流、断路器(可以控制一定次数请求去请求，可配置)、仓壁模式
     * sentinel：超时、限流、断路器(不完备，没有半开态)
     *
     * @return
     */
    @HystrixCommand(fallbackMethod = "fallbackHello")
    @GetMapping("ribbon-hello")
    public String ribbonHello() {
        // 调用provider
        return "ribbon: " + ribbonTemplate.getForObject(
                // 1. 开发体验不好  https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=f&fenlei=256&rsv_pq=a5839e120002ea35&rsv_t=59faLbwWL6wk3baHbXGII2StlHIhRSUB%2Fe%2Be4TdXnZvarfuSROuI6ifY8y4&rqlang=cn&rsv_enter=1&rsv_dl=tb&rsv_sug3=1&rsv_sug1=1&rsv_sug7=100&rsv_sug2=0&rsv_btype=i&prefixsug=f&rsp=8&inputT=198&rsv_sug4=198
                // 2. 复杂的URL很难维护
                // 3. IDE无能为力
                // 4. 可读性差
                "http://provider-demo/hello",
                String.class
        );
    }

    @HystrixCommand
    public String fallbackHello() {
        return "fallback......";
    }

    @Autowired
    private ProviderDemoFeignClient providerDemoFeignClient;

    @GetMapping("feign-hello")
    public String feignHello() {
        return "feign: " + this.providerDemoFeignClient.hello("test");
    }

    @Autowired
    private BaiduFeignClient baiduFeignClient;

    @GetMapping("baidu")
    public String baidu() {
        return this.baiduFeignClient.index();
    }

    @GetMapping("call-404")
    public String call() {
        this.providerDemoFeignClient.haha();
        return "hahahah";
    }

    public void test() {
        try {
            // 监测API最近一段时间内的平均超时时间
            // 监测API最近的QPS
            // 监测API最近的线程数
            // 监测API的错误率、错误次数
            // 任意监测指标达不到配置的值，就抛异常

            // Hystrix: RxJava | Command
            // resilience4j: vavr

            // 原先的业务代码
        } catch (Exception e) {
            // 降级方案
        }
    }
}
